/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details .

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
Date: 18.09.2002
*/

/*
* Author :
* Boris Mikhailenko <stg34@ua.fm>
* Andrey Rakhmanov <andrey_rakhmanov@yahoo.com> - исправление двух багов.
*/

//---------------------------------------------------------------------------
#define _CRT_SECURE_NO_WARNINGS


#include <stdio.h>
#include <stdlib.h>

#include <string.h>

#ifdef WIN32
    #include <winsock2.h>
    #include <windows.h>
    #include <winbase.h>
    #include <winnt.h>
#else
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
#endif

#include "common.h"
#include "proto_unit.h"
#include "blowfish.h"

#define CONNECT         (1)
#define DISCONNECT      (2)
#define BREAK           (3)

//---------------------------------------------------------------------------
string20 packetTypes[12]={
{"CONN_SYN"},
{"CONN_SYN_ACK"},
{"CONN_ACK"},
{"ALIVE_SYN"},
{"ALIVE_ACK"},
{"DISCONN_SYN"},
{"DISCONN_SYN_ACK"},
{"DISCONN_ACK"},
{"FIN"},
{"ERR"},
{"INFO"},
{"INFO_7"}};

#ifndef WIN32
#include <sys/time.h>
void Sleep(int ms)
{
usleep(ms*1000);
}

long GetTickCount()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + tv.tv_usec/1000;
}
#endif

//---------------------------------------------------------------------------
CLIENTPROT::CLIENTPROT(unsigned long i, unsigned short p)
{
int res;
ip = i;
port = p;
CLIENTPROT::onePort = onePort;

memset(&stat, 0, sizeof(stat));
memset(&password, 0, PASSWD_LEN);

#ifdef WIN32
res = WSAStartup(MAKEWORD(2, 0), &wsaData);
#endif

//socks = socket(AF_INET, SOCK_DGRAM, 0);  // Cокет через который шлем
sockr = socket(AF_INET, SOCK_DGRAM, 0);  // Cокет через  который принимаем

localAddrS.sin_family = AF_INET;
localAddrS.sin_port = htons(port - 1);
localAddrS.sin_addr.s_addr = inet_addr("0.0.0.0");

localAddrR.sin_family = AF_INET;
localAddrR.sin_port = htons(port);
localAddrR.sin_addr.s_addr = inet_addr("0.0.0.0");

servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(port);
servAddr.sin_addr.s_addr = ip;

res = bind(sockr, (struct sockaddr*)&localAddrR, sizeof(localAddrR));
if (res == -1)
    {
    errMes = 1;
    return;
    }

#ifdef WIN32
unsigned long arg = 1;
res = ioctlsocket(sockr, FIONBIO, &arg);
#else
if (0 != fcntl(sockr, F_SETFL, O_NONBLOCK))
    {
    printf("fcntl error!\n");
    exit(1);
    }
#endif

userTimeout = 60;
aliveTimeout = 5;
phase = 1;
nonstop = true;
this->start();
}
//---------------------------------------------------------------------------
void CLIENTPROT::RefreshSettings()
{
int res;

#ifndef WIN32
//close(socks);
close(sockr);
#else
//closesocket(socks);
closesocket(sockr);
#endif

//socks = socket(AF_INET, SOCK_DGRAM, 0);  // Cокет через который шлем
sockr = socket(AF_INET, SOCK_DGRAM, 0);  // Cокет через который принимаем

localAddrS.sin_family = AF_INET;
if (onePort)
    localAddrS.sin_port = htons(port);
else
    localAddrS.sin_port = htons(port - 1);
localAddrS.sin_addr.s_addr = inet_addr("0.0.0.0");

localAddrR.sin_family = AF_INET;
localAddrR.sin_port = htons(port);
localAddrR.sin_addr.s_addr = inet_addr("0.0.0.0");

servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(port);
servAddr.sin_addr.s_addr = ip;

res = bind(sockr, (struct sockaddr*)&localAddrR, sizeof(localAddrR));
if (res == -1)
    {
    errMes = 1;
    return;
    }

#ifdef WIN32
unsigned long arg = 1;
res = ioctlsocket(sockr, FIONBIO, &arg);
#else
if (0 != fcntl(sockr, F_SETFL, O_NONBLOCK))
    {
    printf("fcntl error!\n");
    exit(1);
    }
#endif

}
//---------------------------------------------------------------------------
CLIENTPROT::~CLIENTPROT()
{
#ifndef WIN32
//close(socks);
close(sockr);
#else
//closesocket(socks);
closesocket(sockr);
WSACleanup();
#endif
}
//---------------------------------------------------------------------------
int CLIENTPROT::NetRecv()
{
char buffer[512];
int res, pn;
#ifdef WIN32
int fromLen;
#else
socklen_t fromLen;
#endif
char pt[20];
struct sockaddr_in addr;
fromLen = sizeof(addr);
res = recvfrom(sockr, buffer, 512, 0, (struct sockaddr*)&addr, &fromLen);
//-------------------------------------------
//Расшифровать сообщение
unsigned char keyL[PASSWD_LEN];
BLOWFISH_CTX ctx;

//-------------------------------------------

#ifndef WIN32
//
#else
Sleep(20);
#endif

int ret;
ret = -1;
if (res != -1)
    {
    if (strcmp(buffer + 4, "ERR") == 0 || strcmp(buffer + 4, "UPDATE") == 0)
        {
        if (strcmp(buffer + 4, "ERR") == 0)
            pn = ERROR_N;
        else
            pn = UPDATE_N;
        }
    else
        {

        memset(keyL, 0, PASSWD_LEN);
        strncpy((char *)keyL, password, PASSWD_LEN);

        Blowfish_Init(&ctx, keyL, PASSWD_LEN);

        for (int i = 0; i < 64; i++)
            Blowfish_Decrypt(&ctx, (uInt32*)(buffer + i*8), (uInt32*)(buffer + i*8 + 4));

        strncpy(pt, buffer + 4, 20);

        for (int i = 0; i < 12; i++)
            {
            if (strncmp(packetTypes[i], pt, 20) == 0)
                {
                pn = i;
                break;
                }
            }
        }
    switch (pn)
        {
        case CONN_SYN_ACK_N:
            errMes = 3;
            memcpy(&connSynAck, buffer, sizeof(connSynAck));
            rd = connSynAck.rd;
            userTimeout = connSynAck.userTimeOut;
            aliveTimeout = connSynAck.aliveDelay;
            ret = CONN_SYN_ACK_N;
            //pCallBackDirNameFn(connSynAck.dirName, 1);
            emit callBackDirNameFn(connSynAck.dirName, 1);
            break;

        case ALIVE_SYN_N:
            errMes = 3;
            memcpy(&aliveSyn, buffer, sizeof(aliveSyn));
            rd = aliveSyn.rd;
            memcpy(&stat, (char*)aliveSyn.mu, sizeof(stat));
            GetStat(stat);
            ret = ALIVE_SYN_N;
            break;

        case DISCONN_SYN_ACK_N:
            errMes = 3;
            memcpy(&disconnSynAck, buffer, sizeof(disconnSynAck));
            rd = disconnSynAck.rd;
            ret = DISCONN_SYN_ACK_N;
            break;

        case FIN_N:
            errMes = 3;
            ret = FIN_N;
            break;

        case ERROR_N:
            memcpy(&err, buffer, sizeof(err));
            errMes = 3;
            ret = ERROR_N;
            break;

        case INFO_N:
            memcpy(&info, buffer, sizeof(info));
            ret = INFO_N;
            break;

        case INFO_7_N:
            memcpy(&info7, buffer, sizeof(info7));
            ret = INFO_7_N;
            break;

        default:
            ret = ERROR_N;
            WinToKOI(err.text, "Неверный логин или пароль", MAX_MSG_LEN);
            break;
        }
    }
return ret;
}
//---------------------------------------------------------------------------
int CLIENTPROT::NetSend(int n)
{
unsigned char key[PASSWD_LEN];
unsigned char loginS[LOGIN_LEN];
unsigned char keyL[PASSWD_LEN];
unsigned char ciphertext[128];
BLOWFISH_CTX ctx;

char b[2048];
char * buffer = b;
int res;
char str[255];
int msgLen;

memset(buffer, 0, 2048);

if (n == CONN_SYN_N)
    {
    connSyn.protoVer[0] = 0;
    connSyn.protoVer[1] = GetProtoVer();
    msgLen = connSyn.len = Min8(sizeof(CONN_SYN));
    strncpy(connSyn.type, "CONN_SYN", MAX_TYPE_LEN);
    strncpy(connSyn.login, login, LOGIN_LEN);
    memcpy(buffer, &connSyn, sizeof(connSyn));
    }
///////////////////////////////////
if (n == CONN_ACK_N)
    {
    connAck.protoVer[0] = 0;
    connAck.protoVer[1] = GetProtoVer();
    strncpy(connAck.loginS, login, LOGIN_LEN);
    strncpy(connAck.type, "CONN_ACK", MAX_TYPE_LEN);
    rd++;
    connAck.rd = rd;
    msgLen = connAck.len = Min8(sizeof(CONN_ACK));
    memcpy(buffer, &connAck, sizeof(connAck));
    }
//////////////////////////////////
if (n == ALIVE_ACK_N)
    {
    aliveAck.protoVer[0] = 0;
    aliveAck.protoVer[1] = GetProtoVer();
    msgLen = aliveAck.len = Min8(sizeof(ALIVE_ACK));
    strncpy(aliveAck.loginS, login, LOGIN_LEN);
    strncpy(aliveAck.type, "ALIVE_ACK", MAX_TYPE_LEN);
    rd++;
    aliveAck.rd = rd;
    memcpy(buffer, &aliveAck, sizeof(aliveAck));
    }
////////////////////////////////
if (n == DISCONN_SYN_N)
    {
    disconnSyn.protoVer[0] = 0;
    disconnSyn.protoVer[1] = GetProtoVer();
    msgLen = disconnSyn.len = Min8(sizeof(DISCONN_SYN));
    strncpy(disconnSyn.loginS, login, LOGIN_LEN);
    strncpy(disconnSyn.type, "DISCONN_SYN", MAX_TYPE_LEN);
    strncpy(disconnSyn.login, login, 32);
    memcpy(buffer, &disconnSyn, sizeof(disconnSyn));
    }
/////////////////////////////////
if (n == DISCONN_ACK_N)
    {
    disconnAck.protoVer[0] = 0;
    disconnAck.protoVer[1] = GetProtoVer();
    msgLen = disconnAck.len = Min8(sizeof(DISCONN_ACK));
    disconnAck.rd = rd+1;
    strncpy(disconnAck.loginS, login, LOGIN_LEN);
    strncpy(disconnAck.type, "DISCONN_ACK", MAX_TYPE_LEN);
    memcpy(buffer, &disconnAck, sizeof(disconnAck));
    }
//////////////////////////////////*/
#define IA_ID "00100"

strncpy(buffer, IA_ID, 6);
buffer+=6;
buffer+=2;

memset(key, 0, LOGIN_LEN);
memcpy(loginS, login, LOGIN_LEN);
strncpy((char *)key, "pr7Hhen", 8);

Blowfish_Init(&ctx, key, PASSWD_LEN);
for (int i = 0; i < LOGIN_LEN/8; i++)// Шифруем LoginS
    Blowfish_Encrypt(&ctx, (uInt32*)(loginS + i*8), (uInt32*)(loginS + i*8 + 4));

memcpy(buffer, loginS, LOGIN_LEN);
buffer += LOGIN_LEN;

memset(keyL, 0, PASSWD_LEN);
strncpy((char *)keyL, password, PASSWD_LEN);

Blowfish_Init(&ctx, keyL, PASSWD_LEN);

for (int i = 0; i < msgLen/8+1; i++)
    {
    Blowfish_Encrypt(&ctx,
                    (uInt32*)(buffer),
                    (uInt32*)(buffer + 4));
    buffer+=8;
    }
//-------------------------------------
res = sendto(sockr, b, 64, 0, (struct sockaddr*)&servAddr, sizeof(servAddr));// TODO почему длина данных 64?
if (res == -1)
    return 0;

return 0;
}
//---------------------------------------------------------------------------
void CLIENTPROT::Stop()
{
nonstop = false;
}
//---------------------------------------------------------------------------
void CLIENTPROT::Run()
{
int netAct;

netAct = NetRecv();

switch (phase)
    {
    case 1:
        if (buttonAct == CONNECT)
            {
            buttonAct = 0;
            NetSend(CONN_SYN_N);
            phase = 2;
            phaseTime = GetTickCount();
            }
        break;
    case 2:
        if ((GetTickCount() - phaseTime)/1000 > (unsigned)aliveTimeout)
            {
            phase = 1;
            phaseTime = GetTickCount();
            //emit callBackFn(0);
            emit callBackFn(0);

            if (reconnect)
                {
                Sleep(20);
                buttonAct = CONNECT;
                }
            }
        if (buttonAct == DISCONNECT)
            {
            buttonAct = 0;
            NetSend(DISCONN_SYN_N);
            phase = 4;
            phaseTime = GetTickCount();
            }
        if (netAct == CONN_SYN_ACK_N)
            {
            NetSend(CONN_ACK_N);
            phase = 3;
            phaseTime = GetTickCount();
            }

        if (netAct == ERROR_N)
            {
            phase = 1;
            phaseTime = GetTickCount();
            emit callBackFn(0);
            KOIToWin(messageText, err.text, MAX_MSG_LEN);
            errMes = 2;
            }
        break;

    case 3:
        if ((GetTickCount() - phaseTime)/1000 > (unsigned)userTimeout)
            {
            phase = 1;
            phaseTime = GetTickCount();
            emit callBackFn(0);

            if (reconnect)
                {
                Sleep(20);
                buttonAct = CONNECT;
                }
            }

        if (buttonAct == DISCONNECT)
            {
            buttonAct = 0;
            NetSend(DISCONN_SYN_N);
            phase = 4;
            phaseTime = GetTickCount();
            }
        if (netAct == ALIVE_SYN_N)
            {
            emit callBackFn(1);
            NetSend(ALIVE_ACK_N);
            phaseTime = GetTickCount();
            }
        if (netAct == ERROR_N)
            {
            phase = 1;
            phaseTime = GetTickCount();
            }
        if (netAct == INFO_N)
            {
            strncpy(infoText, info.text, MAX_MSG_LEN);
            info.text[MAX_MSG_LEN - 1] = 0;
            emit callBackInfoFn(infoText, info.infoType, 0, 0);
            }

        if (netAct == INFO_7_N)
            {
            emit callBackInfoFn(info7.text, info7.infoType, info7.showTime, info7.sendTime);
            }
        break;
    case 4:
        if ((GetTickCount()-phaseTime)/1000 > (unsigned)aliveTimeout)
            {
            phase=1;
            phaseTime = GetTickCount();
            emit callBackFn(0);
            }

        if (buttonAct == CONNECT)
            {
            buttonAct = 0;
            NetSend(CONN_SYN_N);
            phase = 2;
            phaseTime = GetTickCount();
            }

        if (netAct == DISCONN_SYN_ACK_N)
            {
            NetSend(DISCONN_ACK_N);
            phase = 5;
            phaseTime = GetTickCount();
            }

        if (netAct == ERROR_N)
            {
            phase = 1;
            phaseTime = GetTickCount();
            }
        break;
    case 5:
        if ((GetTickCount() - phaseTime)/1000 > (unsigned)aliveTimeout)
            {
            phase = 1;
            phaseTime = GetTickCount();
            emit callBackFn(0);
            }
        if (buttonAct == CONNECT)
            {
            buttonAct = 0;
            NetSend(CONN_SYN_N);
            phase = 2;
            phaseTime = GetTickCount();
            }
        if (netAct == FIN_N)
            {
            phase = 1;
            phaseTime = GetTickCount();
            emit callBackFn(0);
            }
        if (netAct == ERROR_N)
            {
            phase = 1;
            phaseTime = GetTickCount();
            emit callBackFn(0);
            }
        break;
    }
Sleep(20);
return;
}
//---------------------------------------------------------------------------
void CLIENTPROT::GetStat(LOADSTAT &ls)
{
memcpy(&ls, &stat, sizeof(stat));
}
//---------------------------------------------------------------------------
void CLIENTPROT::SetServer(unsigned long _ip, unsigned short p)
{
ip = _ip;
strncpy(srvIP, inet_ntoa(*((in_addr*)&ip)), 20);
port = p;
}
//---------------------------------------------------------------------------
void CLIENTPROT::SetLogin(char * l)
{
strncpy(login, l, LOGIN_LEN);
}
//---------------------------------------------------------------------------
void CLIENTPROT::SetPassword(char * p)
{
strncpy(password, p, PASSWD_LEN);
}
//---------------------------------------------------------------------------
int CLIENTPROT::Connect()
{
buttonAct = CONNECT;
return 0;
}
//---------------------------------------------------------------------------
int CLIENTPROT::Disconnect()
{
buttonAct = DISCONNECT;
return 0;
}
//---------------------------------------------------------------------------
int CLIENTPROT::GetStatus()
{
return phase;
}
//---------------------------------------------------------------------------
void CLIENTPROT::SetReconnect(int r)
{
reconnect = r;
}
//---------------------------------------------------------------------------
int CLIENTPROT::GetReconnect()
{
return reconnect;
}
//---------------------------------------------------------------------------
void CLIENTPROT::GetMessageText(char * str, int len)
{
strncpy(str, messageText, len);
str[len - 1] = 0;
}
//---------------------------------------------------------------------------
void CLIENTPROT::GetInfoText(char * str, int len)
{
strncpy(str, infoText, len);
str[len - 1] = 0;
}
//---------------------------------------------------------------------------
int CLIENTPROT::GetErrMes()
{
int a = errMes;
errMes = 0;
return a;
}
//---------------------------------------------------------------------------
char CLIENTPROT::GetProtoVer()
{
#define IA_PROTO_VER (7)
return IA_PROTO_VER;
}
//---------------------------------------------------------------------------
#ifndef WIN32
void CLIENTPROT::run()
{
static int a = 0;
if (a == 0)
    {
    usleep(50000);
    a = 1;
    }
while (this->GetNonstop())
    {
    this->Run();
    }
}
#else
//---------------------------------------------------------------------------
void CLIENTPROT::run()
{
while (this->GetNonstop())
    this->Run();
}
//---------------------------------------------------------------------------
#endif

